Essential Rust Syntax Explained
Attention
For an amazing article on collections in Rust, their complexities, and when/why to use them read the docs for
std::collectionshere.
if let
When extracting inner content from an enum, such as a Result or Option, if you're only interested in one of the variants then it's cleaner to use an if let statement. For example,
// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
process::exit(1);
}// this function returns Result<(), Error>
let result = function_that_returns_result();
// `if let` only considers the Error variant
if let Err(e) = result {
process::exit(1);
}Read the statement if let Err(e) = result as: If result is an Err variant, pass the error e to the block of code, otherwise skip it.
while let
This syntax is the looping equivalent of if let. It will continue to loop until the while condition fails. For example,
// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
// do some processing
}// returns an Option<T> with Some<T> until the stream
// is exhausted, then it returns None
let byte_stream = get_byte_stream();
while let Some(data) = byte_stream.read_next() {
// do some processing
}Attention
You cannot write to a variable in a loop that you don't own. Make sure to take ownership of a variable before you try to write to it in-place in a loop.
loop
Use this for an infinite loop. It is particularly useful from daemon processes.
dyn
This is used when declaring a trait as a parameter. Since the size of the object implementing the trait can't be known at compile time, it has to be cast onto the heap using Box<dyn Trait> syntax.
.into_*
When you call a method that is prefixed with into, such as .into_iter(), it consumes the original object, returning the requested type, in this case, an iterator. This is distinct from the raw .iter() call, which instead iterates over a reference, providing a view of the data without consuming it.
Rc and Arc
These are used to make shared pointers to data (reference counted).
Cloning
- Cloning shared pointers is cheap, so don't be afraid
- Always pass a reference when cloning an
RcorArc, e.g.,Rc::clone(&value)
match
You can match against two variables....
fn main() {
let pair = (5, 10);
match pair {
(0, 0) => println!("Both elements are zero"),
(_, 0) => println!("The second element is zero"),
(0, _) => println!("The first element is zero"),
(x, y) => println!("Both elements are non-zero: {}, {}", x, y),
}
}fn main() {
let pair = (5, 10);
match pair {
(0, 0) => println!("Both elements are zero"),
(_, 0) => println!("The second element is zero"),
(0, _) => println!("The first element is zero"),
(x, y) => println!("Both elements are non-zero: {}, {}", x, y),
}
}